﻿using System;
using System.Collections.Generic;
using USClientKernel.Template;
using USNetFramework.Network;
using USNetFramework.RPC;
using USNetFramework.PacketBuilder;
using NetFramework.HTTP;
using Sproto;
using UnityEngine.Networking;
using UnityEngine;
using Common.Utils.Helper;
using Game;

namespace NetFramework
{
    public enum ConnectState
    {
        Disconnected,
        Connecting,
        Connected,
    }

    public class NetworkManager : Singleton<NetworkManager>
    {
        public ConnectState ConnectState { private set; get; }

        private Dictionary<string, ClientSocket> m_dictSockets;
        private Dictionary<string, Action> m_dictConnetedCB;
        private Dictionary<string, string> m_dictHosts;
        private Dictionary<string, uint> m_dictPorts;
        private NetPacketBuilder m_pBuilder;
        private ClientSocket m_clientSocket;
        private NetworkHandler m_handler;

        private bool m_enableSendHeartbeat;
        private double m_lastSendTime;
        private byte m_heartbeatCount;
        private ushort m_lag;

        private Action m_connectCB;

        protected override void Init()
        {
            m_enableSendHeartbeat = false;
            m_lastSendTime = 0;
            m_heartbeatCount = 0;
            m_lag = 0;

            m_dictSockets = new Dictionary<string, ClientSocket>();
            m_dictConnetedCB = new Dictionary<string, Action>();
            m_dictHosts = new Dictionary<string, string>();
            m_dictPorts = new Dictionary<string, uint>();

            m_pBuilder = new NetPacketBuilder();
            m_pBuilder.Init();
            m_clientSocket = new ClientSocket(m_pBuilder);
            m_handler = new NetworkHandler();
            m_clientSocket.Regiester(m_handler);
            RegisterSocket("root", m_clientSocket);

            ConnectState = ConnectState.Disconnected;
            RegisterSocketEvent(OnSocketEvent);
        }

        public void CreateSocket(string _strName, IPacketBuilder _packetBuilder)
        {
            ClientSocket cs = new ClientSocket(_packetBuilder);
            RegisterSocket(_strName, cs);
        }


        #region Connect
        /// <summary>
        /// 链接到制定的IP地址的指定端口
        /// </summary>
        /// <param name="strIP">指定IP地址</param>
        /// <param name="uiPort">指定端口</param>
        public void Connect(string strIP, uint uiPort, Action cb = null)
        {
            Debug.LogFormat("[{0}]:Connect to->{1}:{2}!", ReflectionHelper.GetCurrentClassName(), strIP, uiPort);
            m_clientSocket.Connect(strIP, uiPort);
            m_dictConnetedCB["root"] = cb;
            m_dictHosts["root"] = strIP;
            m_dictPorts["root"] = uiPort;
            m_connectCB = cb;
            ConnectState = ConnectState.Connecting;
        }

        /// <summary>
        /// 指定strKey的Socket连接指定IP的指定端口
        /// </summary>
        /// <param name="strKey">指定Socket</param>
        /// <param name="strIP">指定IP地址</param>
        /// <param name="uiPort">指定端口</param>
        public void Connect(string strKey, string strIP, uint uiPort, Action cb = null)
        {
            ClientSocket cs = GetSocket(strKey);
            if (null != cs)
            {
                Debug.LogFormat("[{0}]:Connect to->{1}:{2}!", ReflectionHelper.GetCurrentClassName(), strIP, uiPort);
                cs.Connect(strIP, uiPort);
                m_dictConnetedCB[strKey] = cb;
                m_dictHosts[strKey] = strIP;
                m_dictPorts[strKey] = uiPort;
                m_connectCB = cb;
                ConnectState = ConnectState.Connecting;
            }
        }
        #endregion


        #region Disconnect
        /// <summary>
        /// 关闭Socket
        /// </summary>
        public void Disconnect()
        {
            m_lag = 0;
            m_clientSocket.Close();
            ConnectState = ConnectState.Disconnected;
        }

        /// <summary>
        /// 链接到制定的IP地址的指定端口
        /// </summary>
        /// <param name="strKey">指定Socket</param>
        public void Disconnect(string strKey)
        {
            ClientSocket cs = GetSocket(strKey);
            if (null != cs)
            {
                cs.Close();
                ConnectState = ConnectState.Disconnected;
            }
        }
        #endregion


        #region Register ClientSocket ISocketListener
        /// <summary>
        /// 注册ClientSocket到NetWorkManager
        /// </summary>
        /// <param name="strKey">注册的Key</param>
        /// <param name="ptrClientSocket">注册的ClientSocket</param>
        public void RegisterSocket(string strKey, ClientSocket ptrClientSocket)
        {
            if (!m_dictSockets.ContainsKey(strKey))
                m_dictSockets.Add(strKey, ptrClientSocket);
        }

        /// <summary>
        /// 获得键值为Key的ClientSocket
        /// </summary>
        /// <param name="strKey">注册的Key</param>
        public ClientSocket GetSocket(string strKey)
        {
            return m_dictSockets[strKey];
        }


        /// <summary>
        /// 获得ClientSocket的键值
        /// </summary>
        /// <param name="_socket">ClientSocket</param>
        public string GetSocketKey(ClientSocket _socket)
        {
            foreach (var item in m_dictSockets)
            {
                if(item.Value == _socket)
                {
                    return item.Key;
                }
            }
            return null;
        }

        /// <summary>
        /// 注销键值为Key的ClientSocket
        /// </summary>
        /// <param name="strKey">注册的Key</param>
        public void UnRegisterSocket(string strKey)
        {
            if (m_dictSockets.ContainsKey(strKey))
                m_dictSockets.Remove(strKey);
        }

        /// <summary>
        /// 注册ISocketListener到键值为Key的ClientSocket
        /// </summary>
        /// <param name="strKey">注册的Key</param>
        /// <param name="ptrListener">注册的ISocketListener</param>
        public void RegisterSocketListener(string strKey, ISocketListener ptrListener)
        {
            ClientSocket s = GetSocket(strKey);
            if (null != s)
            {
                s.Regiester(ptrListener);
            }
        }

        /// <summary>
        /// 注册ISocketListener到默认的ClientSocket
        /// </summary>
        /// <param name="ptrListener">注册的ISocketListener</param>
        public void RegisterSocketListener(ISocketListener ptrListener)
        {
            m_clientSocket.Regiester(ptrListener);
        }

        /// <summary>
        /// 注销键值为Key的ClientSocket中的ISocketListener
        /// </summary>
        /// <param name="strKey">注册的Key</param>
        /// <param name="ptrListener">注册的ISocketListener</param>
        public void UnRegisterSocketListener(string strKey, ISocketListener ptrListener)
        {
            ClientSocket s = GetSocket(strKey);
            if (null != s)
            {
                s.UnRegiester(ptrListener);
            }
        }

        /// <summary>
        /// 注销默认的ClientSocket中的ISocketListener
        /// </summary>
        /// <param name="ptrListener">注册的ISocketListener</param>
        public void UnRegisterSocketListener(ISocketListener ptrListener)
        {
            m_clientSocket.UnRegiester(ptrListener);
        }
        #endregion


        #region Register socket event handler
        public void RegisterSocketEvent(OnSocketEvent _handler)
        {
            m_handler.RegisterSocketEvent(_handler);
        }

        public void UnregisterSocketEvent(OnSocketEvent _handler)
        {
            m_handler.UnregisterSocketEvent(_handler);
        }

        private void OnSocketEvent(SocketEvent _event, NetSocket _socket)
        {
            Debug.Log("OnSocketEvent->"+ TimeHelper.GetCurrentTimeStamp());
            string key = GetSocketKey((ClientSocket)_socket);
            if (null == key)
                return;

            Action cb = m_dictConnetedCB[key];
            switch (_event)
            {
                case SocketEvent.OnConnected:
                    ConnectState = ConnectState.Connected;
                    cb?.Invoke();
                    break;
                case SocketEvent.OnConnectTimout:
                case SocketEvent.OnDisconnected:
                    ConnectState = ConnectState.Disconnected;
                    string strIP = m_dictHosts[key];
                    uint uiPort = m_dictPorts[key];
                    Connect(key, strIP, uiPort, cb);
                    break;
                default:
                    break;
            }
        }
        #endregion


        #region Register Server Request Handler
        public void RegisterServerRequestHandler(OnReceivedServerRequest _handler)
        {
            RpcManager.Instance.RegisterServerRequestHandler(_handler);
        }

        public void UnregisterServerRequestHandler(OnReceivedServerRequest _handler)
        {
            RpcManager.Instance.UnregisterServerRequestHandler(_handler);
        }
        #endregion


        #region Send Net Packet (sproto)
        /// <summary>
        /// 使用默认的ClientSocket中的发送消息
        /// </summary>
        /// <param name="_msg">发送的消息</param>
        /// <param name="_event">收到消息的回应</param>
        public void Send(SPNetMsg _msg, RpcEvent _event = null)
        {
            Send(null, _msg, _event);
        }


        /// <summary>
        /// 使用默认的ClientSocket中的发送消息
        /// </summary>
        /// <param name="_strKey" > ClientSocket名称 </ param >
        /// <param name="_msg">发送的消息</param>
        /// <param name="_event">收到消息的回应</param>
        public void Send(string _strKey, SPNetMsg _msg, RpcEvent _event = null)
        {
            ClientSocket cs = m_clientSocket;
            if (null != _strKey && m_dictSockets.TryGetValue(_strKey, out cs)) { }
            cs.SendData(_msg, _event);
        }


        /// <summary>
        /// 使用默认的ClientSocket中的发送消息
        /// </summary>
        /// <param name="_msg">发送的消息</param>
        /// <param name="_resopnse">收到消息的回应</param>
        public void Send(SPNetMsg _msg, Action<SprotoObject> _resopnse)
        {
            Send(null, _msg, _resopnse);
        }


        /// <summary>
        /// 使用默认的ClientSocket中的发送消息
        /// </summary>
        /// <param name="_strKey" > ClientSocket名称 </ param >
        /// <param name="_msg">发送的消息</param>
        /// <param name="_resopnse">收到消息的回应</param>
        public void Send(string _strKey, SPNetMsg _msg, Action<SprotoObject> _resopnse)
        {
            ClientSocket cs = m_clientSocket;
            if (null != _strKey && m_dictSockets.TryGetValue(_strKey, out cs)) { }
            Debug.LogFormat("[SOCKET]we romte call server request msg -> {0}", _msg.ProtoName);
            Debug.LogFormat("[SOCKET]send msg -> {0}", _msg.Data.ToString());
            cs.SendData(_msg, (retPacket) => 
            {
                // TODO 错误码处理
                SPNetPacket sp = (SPNetPacket)retPacket;
                if (null != _resopnse && null != sp.Response)
                    _resopnse(sp.Response);
            });
        }
        #endregion


        #region Send Net Packet (protobuf)
        ///// <summary>
        ///// 使用默认的ClientSocket中的发送消息
        ///// </summary>
        ///// <typeparam name="T">ProtoBuf消息类型IMessage<T></typeparam>
        ///// <param name="msgType">发送的消息类型</param>
        ///// <param name="ushort">发送的消息ID</param>
        ///// <param name="msg">发送的消息</param>
        //public void Send<T>(ushort msgType, ushort msgID, T msg)
        //    where T : IMessage<T>
        //{
        //    Send<T>(null, msgType, msgID, msg);
        //}

        ///// <summary>
        ///// 使用指定名称的ClientSocket中的发送消息
        ///// </summary>
        ///// <typeparam name="T">ProtoBuf消息类型IMessage<T></typeparam>
        ///// <param name="strName">ClientSocket名称</param>
        ///// <param name="msgType">发送的消息类型</param>
        ///// <param name="ushort">发送的消息ID</param>
        ///// <param name="msg">发送的消息</param>
        //public void Send<T>(string strName, ushort msgType, ushort msgID, T msg)
        //    where T : IMessage<T>
        //{
        //    ClientSocket cs = m_clientSocket;
        //    if (null != strName && m_dictSockets.TryGetValue(strName, out cs)) { }
        //    NetPacket np = m_pBuilder.Pack<T>(msgType, msgID, msg);
        //    cs.SendData(np);
        //}

        ///// <summary>
        ///// 使用默认的ClientSocket中的发送消息
        ///// </summary>
        ///// <param name="msgType">发送的消息类型</param>
        ///// <param name="ushort">发送的消息ID</param>
        ///// <param name="msg">发送的消息</param>
        //public void Send(ushort msgType, ushort msgID, IMessage msg)
        //{
        //    Send(null, msgType, msgID, msg);
        //}

        ///// <summary>
        ///// 使用指定名称的ClientSocket中的发送消息
        ///// </summary>
        ///// <param name="strName">ClientSocket名称</param>
        ///// <param name="msgType">发送的消息类型</param>
        ///// <param name="ushort">发送的消息ID</param>
        ///// <param name="msg">发送的消息</param>
        //public void Send(string strName, ushort msgType, ushort msgID, IMessage msg)
        //{
        //    ClientSocket cs = m_clientSocket;
        //    if (null != strName && m_dictSockets.TryGetValue(strName, out cs)) { }
        //    NetPacket np = m_pBuilder.Pack(msgType, msgID, msg);
        //    cs.SendData(np);
        //}

        ///// <summary>
        ///// 使用默认的ClientSocket中的发送远程调用
        ///// </summary>
        ///// <typeparam name="T">ProtoBuf消息类型IMessage<T></typeparam>
        ///// <param name="msgType">发送的消息类型</param>
        ///// <param name="ushort">发送的消息ID</param>
        ///// <param name="msg">发送的消息</param>
        ///// <param name="callback">回调函数</param>
        //public bool SendRpc<T>(ushort msgType, ushort msgID, T msg, Action<T> callback)
        //    where T : IMessage<T>, new()
        //{
        //    return SendRpc<T>(null, msgType, msgID, msg, callback);
        //}

        ///// <summary>
        ///// 使用默认的ClientSocket中的发送远程调用
        ///// </summary>
        ///// <typeparam name="T">ProtoBuf消息类型IMessage<T></typeparam>
        ///// <param name="strName">ClientSocket名称</param>
        ///// <param name="msgType">发送的消息类型</param>
        ///// <param name="ushort">发送的消息ID</param>
        ///// <param name="msg">发送的消息</param>
        ///// <param name="callback">回调函数</param>
        //public bool SendRpc<T>(string strName, ushort msgType, ushort msgID, T msg, Action<T> callback)
        //    where T : IMessage<T>, new()
        //{
        //    ClientSocket cs = m_clientSocket;
        //    if (null != strName && m_dictSockets.TryGetValue(strName, out cs)) { }
        //    NetPacket np = m_pBuilder.Pack<T>(msgType, msgID, msg);
        //    return cs.SendRpc(np, (NetPacket p) => {
        //        T response = m_pBuilder.Unpack<T>(p);
        //        if (null != response)
        //            callback(response);
        //    });
        //}

        ///// <summary>
        ///// 使用默认的ClientSocket中的发送远程调用
        ///// </summary>
        ///// <param name="msgType">发送的消息类型</param>
        ///// <param name="ushort">发送的消息ID</param>
        ///// <param name="msg">发送的消息</param>
        ///// <param name="callback">回调函数</param>
        //public bool SendRpc(ushort msgType, ushort msgID, IMessage msg, RpcEvent callback)
        //{
        //    return SendRpc(null, msgType, msgID, msg, callback);
        //}


        ///// <summary>
        ///// 使用默认的ClientSocket中的发送远程调用
        ///// </summary>
        ///// <param name="strName">ClientSocket名称</param>
        ///// <param name="msgType">发送的消息类型</param>
        ///// <param name="ushort">发送的消息ID</param>
        ///// <param name="msg">发送的消息</param>
        ///// <param name="callback">回调函数</param>
        //public bool SendRpc(string strName, ushort msgType, ushort msgID, IMessage msg, RpcEvent callback)
        //{
        //    ClientSocket cs = m_clientSocket;
        //    if (null != strName && m_dictSockets.TryGetValue(strName, out cs)) { }
        //    NetPacket np = m_pBuilder.Pack(msgType, msgID, msg);
        //    return cs.SendRpc(np, callback);
        //}
        #endregion


        #region send HTTP

        /// <summary>
        /// 向服务器提交无表单请求
        /// </summary>
        /// <param name="_url">服务器地址,like 'http://www.my-server.com/'</param>
        /// <param name="_result">请求发起后处理回调结果的委托</param>
        /// <returns></returns>
        public void SnedRequest(string _url, HTTPResult _result)
        {
            HTTPManager.Instance.Get(_url, _result);
        }


        /// <summary>
        /// 向服务器提交有表单请求
        /// </summary>
        /// <param name="_url">服务器地址,like 'http://www.my-server.com/'</param>
        /// <param name="_form">请求的服务器表单</param>
        /// <param name="_result">请求发起后处理回调结果的委托</param>
        /// <returns></returns>
        public void SendRequest(string _url, List<IMultipartFormSection> _form, HTTPResult _result)
        {
            HTTPManager.Instance.Post(_url, _form, _result);
        }

        #endregion


        #region Send Heartbeat
        public void StartSendHearbeat()
        {
            m_heartbeatCount = 0;
            m_enableSendHeartbeat = true;
        }


        public void StopSendHeartbeat()
        {
            m_heartbeatCount = 0;
            m_enableSendHeartbeat = false;
        }

        private void SendHeartbeat()
        {
            if (m_heartbeatCount > 1 && ConnectState.Connected == ConnectState)
            {
                // 已经有两个heartbeat没有收到了
                Debug.Log("这里要处理下断线问题");
                StopSendHeartbeat();
                Disconnect();
                return;
            }

            double now = TimeHelper.GetCurrentTimeStamp();
            if (now - m_lastSendTime < GameDefine.GAME_NETWORK_HEARTBEAT_INTERVAL) return;

            SPNetMsg msg = new SPNetMsg();
            msg.ProtoType = (ushort)MessageType.Socket;
            msg.ProtoName = "heartbeat";
            msg.Data = new SprotoObject(msg.ProtoName);
            SprotoObject clientInfo = new SprotoObject("client_info");
            clientInfo["client_time"] = (long)TimeHelper.GetCurrentTimeStampMillisecond();
            msg.Data["client_info"] = clientInfo;
            if (m_heartbeatCount > 0)
            {
                m_lag += (ushort)((now - m_lastSendTime)/2);
            }
            m_lastSendTime = now;
            Send(msg, RecvHeartbeat);
            m_heartbeatCount += 1;
        }

        private void RecvHeartbeat(SprotoObject obj)
        {
            // Debug.Log(obj.ToString());
            m_heartbeatCount -= 1;
            long serverTime = obj["server_info"]["server_time"];
            long sendTime = obj["client_info"]["client_time"];
            long recvTime = (long)TimeHelper.GetCurrentTimeStampMillisecond();
            m_lag = (ushort)((recvTime - sendTime)/2);

            Debug.LogFormat("client time is {0}", recvTime);
            Debug.LogFormat("server time is {0}", serverTime);
            Debug.LogFormat("server lag is {0}", m_lag);
        }

        public ushort GetNetLag()
        {
            return m_lag;
        }
        #endregion


        // 固定更新
        public void OnFixedUpdate()
        {
            if (m_enableSendHeartbeat)
                SendHeartbeat();
        }

        // 更新所有的Socket
        public void OnUpdate()
        {
            foreach(var item in m_dictSockets)
            {
                item.Value.OnUpdate();
            }
        }

        // 断开所有的Socket
        public void OnExit()
        {
            foreach (var item in m_dictSockets)
            {
                item.Value.Close();
            }
        }
    }
}


